package com.miniserver.tql.database.impl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.miniserver.tql.database.ModelConfiguration;
import com.miniserver.tql.database.model.TNode;
import com.miniserver.util.DKeyDesEncrypt;
import com.miniserver.util.LogUtil;

/**
 * 模板配置类实现 用于读取模板和提供参数给其他类使用 可以简单加密和解密模板文件
 * 
 * @author zzlt104
 * 
 */
public class ModelConfigurationImpl implements ModelConfiguration {
	private static LogUtil log = LogUtil.getLogUtil("ModelConfigurationImpl");
	/** 处理模板参数 */
	private ConcurrentHashMap<String, TNode> methods = new ConcurrentHashMap<String, TNode>();
	/** 需要缓存到memcached的model名称 */
	private HashSet<String> resultCachedModelSet = new HashSet<String>();

	/** 网站的根目录 */
	protected String webRoot;
	/** 数据库连接参数 */
	protected ConcurrentHashMap<String, String> parameters = new ConcurrentHashMap<String, String>();

	public ModelConfigurationImpl(String webRoot) {
		this.webRoot = webRoot;
	}

	@Override
	public String getParamValue(String paramName) {
		if (this.parameters.containsKey(paramName)) {
			return this.parameters.get(paramName);
		} else {
			return "";
		}
	}

	@Override
	public String getWebRoot() {
		return this.webRoot;
	}

	/**
	 * 获取配置文件节点的值
	 * 
	 * @param paramName
	 *            配置名称
	 * @return 配置值
	 */
	@Override
	public TNode get(String modelName) {
		return methods.get(modelName);
	}

	public TNode convert(Node node) {
		ModelImpl result = new ModelImpl(node.getNodeName());
		NamedNodeMap attrs = node.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			String name = attrs.item(i).getNodeName();
			String value = attrs.item(i).getNodeValue();
			result.putAttribute(name, value);
		}
		//
		NodeList nodeList = node.getChildNodes();
		ArrayList<TNode> tnodes = new ArrayList<TNode>();
		for (int i = 0; i < nodeList.getLength(); i++) {
			Node tChild = nodeList.item(i);
			if (tChild.getNodeType() == Node.ELEMENT_NODE) {
				tnodes.add(this.convert(tChild));
			}
		}
		//
		TNode[] nodeArray = new TNode[tnodes.size()];
		result.setChildren(tnodes.toArray(nodeArray));
		return result;
	}

	/**
	 * 加载配置文件,自动解密加密的配置文件， 如果加密的配置文件不存在先从未加密的文件生成加密文件
	 * 
	 * @param modelFile
	 * @throws Exception
	 */
	@Override
	public void load(String modelFile) throws Exception {
		ByteArrayInputStream input = null;
		try {
			File sourceFile = new File(modelFile);
			if (methods.size() > 0) {
				this.methods.clear();
			}
			input = new ByteArrayInputStream(DKeyDesEncrypt.decryptFileToBytes(sourceFile));
			//
			XPath xpath = XPathFactory.newInstance().newXPath();
			Node tData = (Node) xpath.evaluate("/data", new InputSource(input), XPathConstants.NODE);
			NodeList nodes = (NodeList) xpath.evaluate("/data/params/add", tData, XPathConstants.NODESET);
			for (int i = 0; i < nodes.getLength(); i++) {
				Node tNode = nodes.item(i);
				//
				Attr tName = (Attr) tNode.getAttributes().getNamedItem("name");
				if (tName == null) {
					throw new Exception("Invalid data config.");
				}
				//
				Attr tValue = (Attr) tNode.getAttributes().getNamedItem("value");
				if (tValue == null) {
					throw new Exception("Invalid data config.");
				}
				//
				this.parameters.put(tName.getValue(), tValue.getValue());
			}
			//
			nodes = (NodeList) xpath.evaluate("/data/method", tData, XPathConstants.NODESET);
			for (int i = 0; i < nodes.getLength(); i++) {
				Node tNode = nodes.item(i);
				Attr tName = (Attr) tNode.getAttributes().getNamedItem("name");
				if (tName == null)
					throw new Exception("Invalid data config.");
				Attr tCached = (Attr) tNode.getAttributes().getNamedItem("cached");
				if (tCached != null && StringUtils.isNotBlank(tCached.getValue())
						&& "true".compareToIgnoreCase(tCached.getValue()) == 0)
					resultCachedModelSet.add(tName.getValue().toLowerCase());
				methods.put(tName.getValue(), this.convert(tNode));
			}
		} catch (Exception ex) {
			log.error("读取配置文件失败:" + modelFile, ex);
		} finally {
			if (input != null) {
				input.close();
			}
		}
	}
}
